package com.lyun.service.impl;

import cn.hutool.core.date.DateUtil;
import com.aliyuncs.exceptions.ClientException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lyun.bo.*;
import com.lyun.common.*;
import com.lyun.constants.HXConstants;
import com.lyun.constants.RabbitMQConstants;
import com.lyun.constants.RedisKeyConstants;
import com.lyun.dto.*;
import com.lyun.mapper.UserMapper;
import com.lyun.pojo.LoveValue;
import com.lyun.pojo.Settings;
import com.lyun.pojo.User;
import com.lyun.service.*;
import com.lyun.util.RedisUtil;
import com.lyun.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@DubboService
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    //默认的心动值
    private static final int DEFAULT_VALUE = 60;

    @Resource
    private UserMapper userMapper;

    @DubboReference
    private LoveValueService loveValueService;

    @DubboReference
    private BlacklistService blacklistService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private OssTemplate ossTemplate;

    @Resource
    private SendSms sendSms;

    @Resource
    private SettingService settingService;

    @Resource
    private GetTags getTags;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RedisUtil redisUtil;

    @DubboReference
    private EachLoveService eachLoveService;

    @Resource
    private HuanxinTemplate huanxinTemplate;


    @Override
    public User getByPhone(String phone) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getPhone, phone);

        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public boolean createUser(User user) {
        return userMapper.insert(user) > 0;
    }

    @Override
    public boolean updateData(User user) {
        return userMapper.updateById(user) > 0;
    }


    /**
     * @param user
     * @return
     */
    @Override
    public User getTodayBest(User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ne(User::getGender, user.getGender());
        queryWrapper.last("ORDER BY rand() limit 1");
        return userMapper.selectOne(queryWrapper);
    }


    /**
     * 根据条件查询推荐用户
     * 1性别要不同
     * 2年龄差距 ±5
     * 3教育最好相同
     * 4城市最好相同
     * 5按登录时间排序
     * 6不能是黑名单用户
     * todo 实现。
     *
     * @param id  用户id
     * @param dto page pagesize gender lastLogin age education city
     * @return 推荐用户
     */
    @Override
    public PageVO<TodayBestVO> getRecommendationList(Long id, RecommendationDTO dto) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();

        queryWrapper.eq(User::getGender, dto.getGender());
        queryWrapper.ne(User::getId, id);
        queryWrapper.orderByAsc(User::getLastLogin);
        Page<User> userPage = new Page<>(dto.getPage(), dto.getPage());
        userMapper.selectPage(userPage, queryWrapper);

        PageVO<TodayBestVO> pageVo = new PageVO<>();

        pageVo.setCounts(userPage.getTotal());
        pageVo.setPages(userPage.getPages());
        pageVo.setPageSize(dto.getPageSize());
        pageVo.setPage(dto.getPage());

        if (!userPage.getRecords().isEmpty()) {
            List<TodayBestVO> voList = userPage.getRecords().stream().map((item) -> {
                //对UserList 每个对象 赋值缘分值 然后set进TodayBestVo 写成集合
                TodayBestVO vo = new TodayBestVO();
                BeanUtils.copyProperties(item, vo);
                vo.setAvatar(item.getHeader());
                String[] split = item.getTags().split(",");
                List<String> list = new ArrayList<>(Arrays.asList(split));
                vo.setTags(list);

                //从redis中获取心动数据
                Double score = redisUtil.zGetScore(id.toString(), item.getId().toString());
                if (Objects.isNull(score)) {
                    //没有的话查数据库
                    Integer fateValue = loveValueService.selectFateValueByFromIdAndToId(id, item.getId());

                    if (fateValue == null || fateValue == 0) {
                        //数据库也没有的话就写入默认值并通过MQ异步生成心动值
                        vo.setFateValue(DEFAULT_VALUE);

                        LoveValueBo bo = new LoveValueBo();
                        bo.setToId(item.getId());
                        bo.setFromId(id);
                        rabbitTemplate.convertAndSend(
                                RabbitMQConstants.LOVE_VALUE_EXCHANGE_NAME,
                                RabbitMQConstants.LOVE_VALUE_INSERT_KEY,
                                bo);
                    } else {
                        vo.setFateValue(fateValue);
                    }
                } else {
                    vo.setFateValue(score.intValue());
                }

                return vo;
            }).collect(Collectors.toList());
            pageVo.setItems(voList);
        } else {
            pageVo.setItems(new ArrayList<>());
        }
        return pageVo;
    }

    @Override
    public List<User> getListByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(User::getId, ids);
        return userMapper.selectList(queryWrapper);
    }

    @Override
    public PersonalVo getPersonalVoByIdAndUserId(long selfId, Long userId) {
        User user = userMapper.selectById(userId);
        PersonalVo personalVo = new PersonalVo();

        String tags = user.getTags();
        List<String> tagsList = new ArrayList<>();
        if (tags != null) {
            String[] split = tags.split(",");
            tagsList.addAll(Arrays.asList(split));
        }

        Integer fateValue = loveValueService.selectFateValueByFromIdAndToId(selfId, userId);
        if (fateValue == null) {
            LoveValue loveValue = new LoveValue();
            loveValue.setFromId(selfId);
            loveValue.setToId(userId);
            Random ran = new Random();
            int ranNum = ran.nextInt(100) + 1;
            loveValue.setFateValue(ranNum);
            fateValue = ranNum;
            loveValueService.insertValue(loveValue);
        }

        personalVo.setAge(user.getAge());
        personalVo.setId(user.getId());
        personalVo.setGender(user.getGender());
        personalVo.setNickname(user.getNickname());
        personalVo.setAvatar(user.getHeader());
        personalVo.setTags(tagsList);
        personalVo.setFateValue(fateValue);

        return personalVo;
    }

    @Override
    public List<PersonalVo> getRecommendUserList(Long id) {
        User user = userMapper.selectById(id);

        String gender = user.getGender();

        List<PersonalVo> voList = userMapper.selectRecommendPersonalListWithGender(gender);

        voList = voList.stream().map((personalVo) -> {
            Long userId = personalVo.getId();
            String key = EveryConstant.REDIS_USER_UNLIKE + id + "to" + userId;
            //判断redis中是否有拉黑字段
            Object o = redisTemplate.opsForValue().get(key);

            if (Objects.nonNull(o)) {
                //不为空 说明拉黑过 返回空
                return null;
            }

            return personalVo;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        return voList;
    }


    /**
     * 登录
     *
     * @param loginDto 手机号
     */
    @Override
    public void login(LoginDto loginDto) throws ClientException {

        String phone = loginDto.getPhone();

        if (!StringUtils.hasText(phone) || phone.length() != 11) {
            throw new MyBusinessException("请输入正确的手机号");
        }

        //发送验证码
        String code = sendSms.sendMsg(phone);

        //把手机号和验证码存入redis
        log.info("user:login: {}验证码为：{}", phone, code);

        redisTemplate.opsForValue().set(phone, code, 300, TimeUnit.SECONDS);
    }

    /**
     * @param bo
     * @return
     */
    @Override
    public boolean loginReginfo(LoginReginfoBo bo) throws ParseException {
        User user = new User();
        BeanUtils.copyProperties(bo, user);
        user.setMarriage(0);
        int age = DateUtil.age(bo.getBirthday(), new Date());
        //int age = GetAge.getAgeForBirthday(sdf.format(bo.getBirthday()));
        user.setAge(age);
        //TODO list列表 按我之前说的type handler修改
        user.setTags(getTags.getTags(user).toString().replace("[", "").replace("]", "").trim());
        return this.updateData(user);
    }

    /**
     * 更新头像
     *
     * @param bo
     */
    @Override
    public void loginReginfoHead(LoginReginfoHeadBo bo) throws IOException {
        String upload = ossTemplate.upload(bo.getHeader());

        if (!StringUtils.hasText(upload)) {
            return;
        }

        User user = new User();
        user.setId(bo.getId());
        user.setHeader(upload);
        this.updateData(user);
    }

    /**
     * 修改手机号，发送验证码
     *
     * @param phone
     * @throws ClientException
     */
    @Override
    public void sendVerificationCode(String phone) throws ClientException {
        String code = sendSms.sendMsg(phone);
        redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
        log.info("user:update:phone: " + phone + "验证码为：" + code);
    }

    /**
     * 获取粉丝列表，关注用户等
     *
     * @param bo : type : 1 互相关注 2 我关注，3 粉丝，4 谁看过我
     * @return
     */
    @Override
    public PageVO<FriendsVo> getFriendsVoList(FriendsBo bo) {

        switch (bo.getType()) {
            case 1:
                return getEachLoves(bo.getUserId(), bo.getPage(), bo.getPageSize());
            case 2:
                return getLoves(bo.getUserId(), bo.getPage(), bo.getPageSize());
            case 3:
                return getFans(bo.getUserId(), bo.getPage(), bo.getPageSize());
            case 4:
                //todo 访客列表，没事做了再说
                return getVisitors();
            default:
                return new PageVO<>();
        }
    }


    /**
     * 互相关注
     *
     * @param userId   当前用户id
     * @param page
     * @param pageSize
     */
    private PageVO<FriendsVo> getEachLoves(Long userId, Integer page, Integer pageSize) {
        EachLoveBo eachLoveBo = new EachLoveBo();
        eachLoveBo.setUserId(userId);
        eachLoveBo.setPage(page);
        eachLoveBo.setPageSize(pageSize);
        List<Long> eachLoveUserIdsList = eachLoveService.getEachLoveIdsList(eachLoveBo);

        if (CollectionUtils.isEmpty(eachLoveUserIdsList)) {
            return new PageVO<>();
        }

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(User::getId, eachLoveUserIdsList);
        List<User> userList = userMapper.selectList(queryWrapper);

        List<FriendsVo> voList = userList.stream().map(item -> {
            FriendsVo vo = new FriendsVo();
            BeanUtils.copyProperties(item, vo);

            vo.setAvatar(item.getHeader());
            //互相关注列表，肯定是关注了的，不需要判断
            vo.setAlreadyLove(true);
            return vo;
        }).collect(Collectors.toList());

        PageVO<FriendsVo> pageVo = new PageVO<>();
        pageVo.setItems(voList);
        return pageVo;
    }

    /**
     * 喜欢列表
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    private PageVO<FriendsVo> getLoves(Long userId, Integer page, Integer pageSize) {
        //直接用redis实现
        String key = RedisKeyConstants.LOVE_USER_PREFIX + userId;
        Long count = redisUtil.zSize(key);
        int start = (page - 1) * pageSize;
        int end = page * pageSize - 1;

        if (count == 0 || start > count) {
            return new PageVO<>();
        }

        if (end > count) {
            end = count.intValue();
        }

        Set<Object> lovedUserIdSet = redisUtil.zReverseRange(key, start, end);
        List<Long> lovedUserIdList = lovedUserIdSet.stream().map(item -> Long.valueOf(item.toString())).collect(Collectors.toList());

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(User::getId, lovedUserIdList);
        List<User> userList = userMapper.selectList(queryWrapper);

        List<FriendsVo> voList = userList.stream().map(item -> {
            FriendsVo vo = new FriendsVo();
            BeanUtils.copyProperties(item, vo);

            vo.setAvatar(item.getHeader());
            //喜欢列表，肯定是喜欢的，不需要判断
            vo.setAlreadyLove(true);
            return vo;
        }).collect(Collectors.toList());

        PageVO<FriendsVo> pageVo = new PageVO<>();
        pageVo.setItems(voList);
        return pageVo;
    }

    /**
     * 粉丝
     */
    private PageVO<FriendsVo> getFans(Long userId, Integer page, Integer pageSize) {
        FriendsBo bo = new FriendsBo();
        bo.setPage(page);
        bo.setPageSize(pageSize);
        bo.setUserId(userId);
        List<Long> fansIdList = eachLoveService.getFanIdsListWithPage(bo);

        if (CollectionUtils.isEmpty(fansIdList)) {
            return new PageVO<>();
        }

        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(User::getId, fansIdList);
        List<User> userList = userMapper.selectList(queryWrapper);

        String key = RedisKeyConstants.LOVE_USER_PREFIX + userId;

        boolean flag = true;
        Set<Object> loveList = redisUtil.zRange(key, 0, -1);
        if (CollectionUtils.isEmpty(loveList)) {
            //如果集合为空，说明无喜欢信息，接下来的所有无需判断皆为false
            flag = false;
        }

        boolean finalFlag = flag;
        List<FriendsVo> voList = userList.stream().map(item -> {
            FriendsVo vo = new FriendsVo();
            BeanUtils.copyProperties(item, vo);

            vo.setAvatar(item.getHeader());
            //去set集合中查看item的id在不在其中，
            // 在说明喜欢 不在则为不喜欢
            vo.setAlreadyLove(finalFlag && loveList.contains(item.getId()));
            return vo;
        }).collect(Collectors.toList());

        PageVO<FriendsVo> pageVo = new PageVO<>();
        pageVo.setItems(voList);
        return pageVo;
    }

    /**
     * 访客
     */
    private PageVO<FriendsVo> getVisitors() {
        return new PageVO<>();
    }

    /**
     * 登录并验证是否是新用户，并赋予token
     * -> 如果是新用户，需要给新用户默认的通用设置
     *
     * @param dto 手机号，验证码
     */
    @Override
    public LoginVerificationVo loginVerification(LoginVerificationDto dto) {

        if (!StringUtils.hasText(dto.getPhone()) || !StringUtils.hasText(dto.getVerificationCode())) {
            throw new MyBusinessException("手机号或验证码不能为空");
        }

        //第一步：把获取到的手机号和验证码和redis里的作比较
        String phone = dto.getPhone();

        String code = redisTemplate.opsForValue().get(phone);
        if (!dto.getVerificationCode().equals(code)) {
            throw new MyBusinessException("验证码错误");
        }

        //到这里说明验证码对了
        // 第二步，查询这个手机号是不是新用户
        LoginVerificationVo vo = new LoginVerificationVo();
        User user = this.getByPhone(phone);
        HashMap<String, Object> map = new HashMap<>();

        if (Objects.isNull(user)) {

            vo.setIsNew(true);
            //是新用户 创建新用户
            User newUser = new User();
            newUser.setPhone(phone);
            newUser.setLastLogin(String.valueOf(System.currentTimeMillis()));
            userMapper.insert(newUser);

            //第三步，创建token
            //map里放id和phone
            Long id = newUser.getId();
            map.put("id", id);
            map.put("phone", phone);
            String token = JwtUtils.getToken(map);

            vo.setToken(token);

            //设置默认属性
            //todo 消息队列
            Settings settings = new Settings();
            settings.setUserId(id);
            settings.setPhone(phone);
            settings.setLikeNotification(true);
            settings.setGonggaoNotification(true);
            settings.setPinglunNotification(true);
            settingService.saveSettings(settings);

            huanxinTemplate.createUser(HXConstants.HX_HEADER + newUser.getId(), HXConstants.DEFAULT_PASSWORD);
            return vo;
        }

        vo.setIsNew(false);
        user.setLastLogin(String.valueOf(System.currentTimeMillis()));
        this.updateData(user);
        map.put("id", user.getId());
        map.put("phone", user.getPhone());
        String token = JwtUtils.getToken(map);
        vo.setToken(token);
        log.info("token : " + token);
        return vo;
    }


    /**
     * 获取三个数量
     * 1. 互相喜欢数
     * 2. 喜欢数
     * 3. 粉丝数
     */
    @Override
    public LoveCountVo getCounts(Long userId) {
        LoveCountVo vo = new LoveCountVo();

        String eachLoveCountKey = RedisKeyConstants.USER_EACH_LOVE_COUNT + userId;
        String eachLoveCount = redisTemplate.opsForValue().get(eachLoveCountKey);
        vo.setEachLoveCount(StringUtils.hasText(eachLoveCount) ? Integer.parseInt(eachLoveCount) : 0);

        String loveCountKey = RedisKeyConstants.USER_LOVE_COUNT + userId;
        String loveCount = redisTemplate.opsForValue().get(loveCountKey);
        vo.setLoveCount(StringUtils.hasText(loveCount) ? Integer.parseInt(loveCount) : 0);

        String fanCountKey = RedisKeyConstants.USER_FANS_COUNT + userId;
        String fanCount = redisTemplate.opsForValue().get(fanCountKey);
        vo.setFanCount(StringUtils.hasText(fanCount) ? Integer.parseInt(fanCount) : 0);

        return vo;
    }

    @Override
    public LoveCountVo getCountsStandby(Long userId) {
        return eachLoveService.selectCount(userId);
    }

    @Override
    public User findByHuanxin(String huanxinId) {
        return null;
    }

    /**
     * 管理端-分页查询用户列表
     *
     * @param dto 姓名、状态
     * @return
     */
    @Override
    public PageVO<AdminUserVO> adminGetAll(AdminUserDTO dto) {
        Page<User> page = new Page<>(dto.getPage(), dto.getPageSize());
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dto.getStatus() != null, User::getStatus, dto.getStatus());
        queryWrapper.like(StringUtils.hasText(dto.getName()), User::getNickname, dto.getName());
        userMapper.selectPage(page, queryWrapper);
        List<User> resultList = page.getRecords();

        if (CollectionUtils.isEmpty(resultList)) {
            return new PageVO<>();
        }

        List<AdminUserVO> voList = resultList.stream().map(item -> {
            AdminUserVO vo = new AdminUserVO();
            BeanUtils.copyProperties(item, vo);
            vo.setId(item.getId().toString());
            return vo;
        }).collect(Collectors.toList());

        PageVO<AdminUserVO> vo = new PageVO<>();
        vo.setCounts(page.getTotal());
        vo.setItems(voList);
        vo.setPageSize(dto.getPageSize());
        return vo;
    }

    /**
     * 更新用户状态
     *
     * @param dto
     */
    @Override
    public void auditSmallVideo(AdminAbledUserDTO dto) {
        User user = userMapper.selectById(Long.valueOf(dto.getUserId()));
        if (user == null) {
            throw new MyBusinessException("该用户不存在");
        }
        user.setStatus(dto.getStatus());
        userMapper.updateById(user);
    }

}